//
// Created by twetec on 25-7-12.
//
#include <network_prober/backup/backup_task_handler.h>

#include <coroutine>

#include <boost/asio/ip/address.hpp>
#include <libcron/Cron.h>
#include <libcron/CronData.h>

#include <network_prober/database/memory_db_holder.h>
#include <network_prober/rpc/perception_rpc_client.h>

#include "handle_telnet_backup_task.h"
#include "handle_ssh_backup_task.h"

using namespace std::chrono_literals;

namespace network_prober::backup
{
class HandleBackupTaskCoroutine;

// 当前批次观测任务完成后，将观测结果存入数据库
// 观测任务集
static std::vector<HandleBackupTaskCoroutine> s_backupTaskCoroutines;
// 观测结果集 (一个任务对应一个结果)
std::vector<database::BackupTaskResultModel> s_backupTaskResults;
// 加锁，等待观测任务结束
std::mutex s_backupTaskResultsMutx;
std::condition_variable s_backupTaskResultsCV;

class Promise;

class HandleBackupTaskCoroutine
{
public:
    using promise_type = Promise;

public:
    HandleBackupTaskCoroutine(std::coroutine_handle<promise_type> coroutine) : coroutineHandle(coroutine)
    {}

    HandleBackupTaskCoroutine(const HandleBackupTaskCoroutine&) = delete;
    void operator=(const HandleBackupTaskCoroutine&) = delete;

    HandleBackupTaskCoroutine(HandleBackupTaskCoroutine&& coroutine) noexcept
        : coroutineHandle(std::exchange(coroutine.coroutineHandle, nullptr)) {}

    HandleBackupTaskCoroutine& operator=(HandleBackupTaskCoroutine&& coroutine) noexcept
    {
        coroutineHandle = std::exchange(coroutine.coroutineHandle, nullptr);
        return *this;
    }

public:
    // 协程promise对象控制
    std::coroutine_handle<promise_type> coroutineHandle;
};

class Promise
{
public:
    HandleBackupTaskCoroutine get_return_object()
    {
        return HandleBackupTaskCoroutine(std::coroutine_handle<Promise>::from_promise(*this));
    }

    std::suspend_never initial_suspend() { return {}; } // 协程启动时调用
    // std::suspend_always yield_value() { return {}; }    // 协程函数中有co_yield时调用
    void unhandled_exception() {}   // 协程函数发生异常时调用
    // void return_void() {}    // 协程函数

    void return_value(const database::BackupTaskResultModel& backupTaskResult)   // 协程函数中有co_return 有值时调用
    {
        std::unique_lock<std::mutex> backupTaskResultsLock(s_backupTaskResultsMutx);
        s_backupTaskResults.push_back(std::move(backupTaskResult));
        s_backupTaskResultsCV.notify_one();
    }

    std::suspend_never final_suspend() noexcept { return {}; }  // 协程结束时调用

    // GCC11bug 需补上
    template <typename U>
    U&& await_transform(U&& awaitable) noexcept { return static_cast<U&&>(awaitable);}

private:

};

// HandleBackupTaskCoroutine handleTELNETBackupTaskIPv4(const database::BackupTaskModel& backupTask)
// {
//     // 封装备份对象
//     HandleTELNETBackupIPv4Awaiter awaiter{backupTask};
//
//     // 发起备份
//     std::string backupData = co_await awaiter;
//
//     // 备份结果
//     database::BackupTaskResultModel backupTaskResult;
//
//     // 备份目标标识
//     backupTaskResult.uuid = backupTask.uuid;
//     // 备份数据
//     backupTaskResult.backupData = backupData;
//     // 备份时间
//     backupTaskResult.backupDate = std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count();
//
//     // 返回备份数据
//     co_return backupTaskResult;
// }
//
// HandleBackupTaskCoroutine handleTELNETBackupTaskIPv6(const database::BackupTaskModel& backupTask)
// {
//     // 封装备份对象
//     HandleTELNETBackupIPv4Awaiter awaiter{backupTask};
//
//     // 发起备份
//     std::string backupData = co_await awaiter;
//
//     // 备份结果
//     database::BackupTaskResultModel backupTaskResult;
//
//     // 备份目标标识
//     backupTaskResult.uuid = backupTask.uuid;
//     // 备份数据
//     backupTaskResult.backupData = backupData;
//     // 备份时间
//     backupTaskResult.backupDate = std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count();
//
//     // 返回备份数据
//     co_return backupTaskResult;
// }
//
// HandleBackupTaskCoroutine handleSSHBackupTaskIPv4(const database::BackupTaskModel& backupTask)
// {
//     // 封装备份对象
//     HandleSSHBackupIPv4Awaiter awaiter{backupTask};
//
//     // 发起备份
//     std::string backupData = co_await awaiter;
//
//     // 备份结果
//     database::BackupTaskResultModel backupTaskResult;
//
//     // 备份目标标识
//     backupTaskResult.uuid = backupTask.uuid;
//     // 备份数据
//     backupTaskResult.backupData = backupData;
//     // 备份时间
//     backupTaskResult.backupDate = std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count();
//
//     // 返回备份数据
//     co_return backupTaskResult;
// }
//
// HandleBackupTaskCoroutine handleSSHBackupTaskIPv6(const database::BackupTaskModel& backupTask)
// {
//     // 封装备份对象
//     HandleSSHBackupIPv6Awaiter awaiter{backupTask};
//
//     // 发起备份
//     std::string backupData = co_await awaiter;
//
//     // 备份结果
//     database::BackupTaskResultModel backupTaskResult;
//
//     // 备份目标标识
//     backupTaskResult.uuid = backupTask.uuid;
//     // 备份数据
//     backupTaskResult.backupData = backupData;
//     // 备份时间
//     backupTaskResult.backupDate = std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count();
//
//     // 返回备份数据
//     co_return backupTaskResult;
// }

template<class T>
HandleBackupTaskCoroutine handleBackupTask(const database::BackupTaskModel& backupTask)
{
    // 备份结果
    database::BackupTaskResultModel backupTaskResult {
        .uuid = backupTask.uuid,
        .backupData = "",
        .backupDate = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(),
    };

    // 封装备份对象
    T awaiter{backupTask};

    // 发起备份
    std::string backupData = co_await awaiter;

    // 备份数据
    backupTaskResult.backupData = backupData;
    // 备份时间
    backupTaskResult.backupDate = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();

    SPDLOG_INFO("备份目标：{} ------ 备份内容：{}", backupTaskResult.uuid, backupTaskResult.backupData);

    // 返回备份数据
    co_return backupTaskResult;
}

void BackupTaskHandler::_startTaskDispatch()
{
    // 定时任务控制对象
    libcron::Cron cron;

    // 给定时任务添加一个工作计划 [58 * * * * ?] -> 从右往左读 -> 无效周几 每年 每月 每日 每时 每分 的第58秒 执行
    // 等2个0s周期后，观测任务就开始执行
    cron.add_schedule("BACKUP_TASK_DISPATCH", "30 * * * * ?", [&](const libcron::TaskInformation& taskInfo) {
        // 获取到任务延迟执行的时间 如果执行时间比计划时间完5s，表示需要调整计划, 则抛出异常
        if (taskInfo.get_delay() > 5s) {
            // 移除定时任务的所有工作计划
            cron.clear_schedules();
            // 日志异常
            std::stringstream ss;
            ss << "备份任务分发线程延迟时间过长：" << taskInfo.get_delay().count() << "秒";
            SPDLOG_ERROR(ss.str());
            throw std::runtime_error(ss.str());
        }

        // 获取所有需要备份的设备
        std::vector<database::DeviceModel> devices = database::MemoryDBHolder::getInstance().getAllDevices();

        // 如果备份的设备还不存在，则直接返回，等待下一次查看
        if (devices.empty()) return;

        // 备份任务
        std::vector<database::BackupTaskModel> backupTasks;
        backupTasks.reserve(devices.size());

        // 获取当前系统时间
        std::chrono::time_point<std::chrono::system_clock> sysNow = std::chrono::system_clock::now();
        // 从系统时间中提取出：年月日
        // 将begin代表的时刻，向过去取整到最近的号数
        std::chrono::time_point<std::chrono::system_clock, std::chrono::days> nowDayStart = std::chrono::floor<std::chrono::days>(sysNow);
        // 转化为
        std::chrono::year_month_day nowYMD{nowDayStart};
        // 记录年，月，日
        std::int32_t nowYear{nowYMD.year()};
        std::uint32_t nowMonth{nowYMD.month()};
        std::uint32_t nowDay{nowYMD.day()};

        // 循环遍历每一个观测设备，针对每一个观测设备，制定下一局(60s)的观测任务
        for (auto it = devices.begin(); it != devices.end(); ++it) {
            // 判断当前目标的备份时间是否迟于当前时间，如果迟于当前时间，则不用备份，跳过
            std::stringstream ss;
            ss << nowYear << "-" << nowMonth << "-" << nowDay << " " << it->backupDate;
            // 解析日期字符串->time_point
            std::tm tm = {};
            std::istringstream cc(ss.str());
            cc >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
            std::time_t tt = std::mktime(&tm);
            std::chrono::time_point<std::chrono::system_clock> backupTime = std::chrono::system_clock::from_time_t(tt);

            // 如果备份时间晚于当前时间，则跳过当前目标
            if (backupTime > sysNow) continue;

            // 如果当前时间，晚于应备份时间，表示当前目标可以备份，再查看之前是否备份过
            database::BackupTaskResultModel backupTaskResult = database::MemoryDBHolder::getInstance().getBackupTaskResultByUUID(it->uuid);
            // 如果上次备份时间晚于当前应备份时间，表示该目标当前已经备份过了,则跳过
            if (backupTaskResult.backupDate > std::chrono::time_point_cast<std::chrono::seconds>(backupTime).time_since_epoch().count()) continue;

            // 如果当前目标的备份时间不晚于当前时间，则制定备份任务
            // 备份任务
            database::BackupTaskModel backupTask;

            backupTask.uuid = it->uuid;    // 设备标识
            backupTask.interface = it->interface;    // 网口名称
            backupTask.address = it->address;  // ip地址
            backupTask.backupDate = it->backupDate; // 备份时间
            backupTask.command = it->command;   // 备份命令
            backupTask.protocol = it->protocol;  // 协议 （1:TeInet 2: SSH）
            backupTask.account = it->account;    // 账号
            backupTask.password = it->password;    // 密码
            backupTask.privilegedPassword = it->privilegedPassword;  // 请求体
            backupTask.port = it->port; // 端口

            backupTasks.push_back(std::move(backupTask));
        }

        // 如果有需要备份的目标时，全部分发到备份任务表中
        if (!backupTasks.empty()) {
            database::MemoryDBHolder::getInstance().batchInsertBackupTasks(backupTasks);
        }
    });

    // 作为定时任务的时钟，进行定时
    while(true) {
        cron.tick(); // 保证至少每秒调用一次，避免错过任何调度
        std::this_thread::sleep_for(500ms); // 等待500ms,再调度一次工作计划
    }
}

void BackupTaskHandler::_startTaskHandle()
{
    // 不断查看是否存在备份任务，如果存在则执行任务，不存在则继续查看
    while (true) {
        // 获取所有备份任务
        std::vector<database::BackupTaskModel> backupTasks = database::MemoryDBHolder::getInstance().getAllBackupTasks();

        // 当还不存在备份任务时，暂停一会，再去查
        if (backupTasks.empty()) {
            std::this_thread::sleep_for(3s);
            continue;
        }

        // 备份任务取出后，清空缓存
        database::MemoryDBHolder::getInstance().batchRemoveAllBackupTasks();

        // 存在备份任务时，遍历执行每一个备份任务
        for (auto it = backupTasks.begin(); it != backupTasks.end(); ++it) {
            // 获取网口ip地址对象
            boost::asio::ip::address interfaceIPAddress = boost::asio::ip::make_address(it->interface);

            // TELNET协议
            if (it->protocol == database::BackupProtocol::TEL_NET) {
                if (interfaceIPAddress.is_v4()) {
                    // 调用协程函数，完成一次任务
                    s_backupTaskCoroutines.push_back(handleBackupTask<HandleTELNETBackupIPv4Awaiter>(*it));
                }
                else if (interfaceIPAddress.is_v6()) {
                    // 调用协程函数，完成一次任务
                    s_backupTaskCoroutines.push_back(handleBackupTask<HandleTELNETBackupIPv6Awaiter>(*it));
                }
            }
            // SSH协议
            else if (it->protocol == database::BackupProtocol::SSH) {
                if (interfaceIPAddress.is_v4()) {
                    // 调用协程函数，完成一次任务
                    s_backupTaskCoroutines.push_back(handleBackupTask<HandleSSHBackupIPv4Awaiter>(*it));
                }
                else if (interfaceIPAddress.is_v6()) {
                    // 调用协程函数，完成一次任务
                    s_backupTaskCoroutines.push_back(handleBackupTask<HandleSSHBackupIPv6Awaiter>(*it));
                }
            }
        }

        // 等待当前批次备份任务完成
        std::unique_lock<std::mutex> backupTaskResultsLock(s_backupTaskResultsMutx);
        s_backupTaskResultsCV.wait(backupTaskResultsLock, [] { return s_backupTaskResults.size() >= s_backupTaskCoroutines.size(); });

        // 将当前批次的备份结果于上一次做对比，如果有不同，则做更新
        // std::vector<database::BackupTaskResultModel> lastBackupTaskResults = database::MemoryDBHolder::getInstance().getAllBackupTaskResults();

        // 任务协程
        s_backupTaskCoroutines.clear();

        // 要上报的备份结果
        std::vector<database::BackupTaskResultModel> reportedBackupTaskResults;

        // 遍历备份的结果，于上一次结果进行比对
        for (auto it = s_backupTaskResults.begin(); it != s_backupTaskResults.end(); ++it) {
            // 更新备份结果
            database::MemoryDBHolder::getInstance().updateBackupTaskResult(it->uuid, it->backupData, std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count());
            // 不比较备份数据，直接归集上报
            reportedBackupTaskResults.push_back(std::move(*it));

            // // 遍历上一次备份结果
            // for (auto lastIt = lastBackupTaskResults.begin(); lastIt != lastBackupTaskResults.end(); ++lastIt) {
            //     // 如果不是同一个目标，则跳过
            //     if (it->uuid != lastIt->uuid) continue;
            //
            //     // 当比对的是同一个目标时，更新备份结果
            //     database::MemoryDBHolder::getInstance().updateBackupTaskResult(it->uuid, it->backupData, std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()).time_since_epoch().count());
            //
            //     // 不比较备份数据，直接归集上报
            //     reportedBackupTaskResults.push_back(std::move(*it));
            //     // 如果两次备份的内容不同，则进行归集上报
            //     // if (it->backupData != ij->backupData) {
            //     //     reportedBackupTaskResults.push_back(std::move(*it));
            //     // }
            // }
        }

        // 如果不存在要上报的备份数据，则退出
        if (!reportedBackupTaskResults.empty()) {
            SPDLOG_INFO("开始上报本轮备份结果..");
            // 当前批次的备份任务完成后，上报平台（异步）
            rpc::PerceptionRPCClient::getInstance().reportBackupTaskResultsToPerception(reportedBackupTaskResults);
            SPDLOG_INFO("本轮备份结果上报完成..");
        }

        // 清空缓存的备份任务执行结果
        s_backupTaskResults.clear();
    }
}

void BackupTaskHandler::start()
{
    // 开始任务分发
    std::jthread taskDispatchThread(&BackupTaskHandler::_startTaskDispatch, this);
    // 开始任务处理
    std::jthread taskHandle(&BackupTaskHandler::_startTaskHandle, this);
}

BackupTaskHandler &BackupTaskHandler::getInstance()
{
    static BackupTaskHandler instance;
    return instance;
}

BackupTaskHandler::BackupTaskHandler()
{
    s_backupTaskResults.reserve(10000);
    s_backupTaskCoroutines.reserve(10000);
}

BackupTaskHandler::~BackupTaskHandler()
{
}
}
